package userManage

import (
	"context"
	"errors"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"log"
	"main/src/apps"
	"main/src/configs"
	"main/src/utils"
	"time"
)

type LoginDto struct {
	Username string `json:"username" binding:"required,min=6,max=32" label:"用户名"`
	Password string `json:"password" binding:"required,min=6,max=32" label:"密码"`
}

// ValidateUser 验证JSON表单, 包括是否查询到用户，密码是否正确，数据库是否正常
func (dto *LoginDto) ValidateUser() (token LoginResponseDto, err error, errStatus int) {
	var user = &User{}
	result := apps.DB.Where("username = ?", dto.Username).Find(&user)
	log.Print(result.Error)
	if errors.Is(result.Error, gorm.ErrRecordNotFound) {
		// Found
		err = errors.New("数据库错误:" + result.Error.Error())
		errStatus = utils.Status.ServerError
	} else if result.Error != nil {
		err = errors.New("没有该用户名:" + dto.Username)
		errStatus = utils.Status.NotFound
	} else if user.IsBackUp {
		err = errors.New("已停用该用户名:" + dto.Username)
		errStatus = utils.Status.AuthError
	} else {
		if user.CheckedPassword(dto.Password) {
			// token处理，在redis存储
			var jwtInfo utils.UserJWTInfo
			var jwtToken string
			user.Export(&jwtInfo)
			jwtToken, err = utils.GenToken(&jwtInfo)

			if err != nil {
				return LoginResponseDto{}, err, utils.Status.Error
			}
			var jwtUser utils.UserJWTInfo
			user.Export(&jwtUser)
			var res = apps.RedisClient.Set(context.Background(), jwtToken, &jwtUser, time.Minute*time.Duration(configs.Config.RedisTokenLiveTime)).Err()
			log.Println(res)
			// token处理，在redis存储
			token = LoginResponseDto{
				Token:    "Bearer " + jwtToken,
				Username: user.Username,
				IsSuper:  user.IsSuper,
			}
		} else {
			err = errors.New("密码错误: username = " + dto.Username)
			errStatus = utils.Status.Error
		}
	}

	return token, err, errStatus
}

type LoginResponseDto struct {
	Token    string `json:"token"`
	Username string `json:"username" binding:"required,min=6,max=32"`
	IsSuper  bool   `json:"is_super"`
}

type GetUserDto struct {
	utils.GetDataLimitDto
	Username string `json:"username"`
}

func (dto *GetUserDto) Get(c *gin.Context) (responseDto utils.GetDataLimitResponseDto, err error) {
	dto.Init()
	if dto.PageNum < 0 {
		utils.Response(c, "page_num < 0", utils.Status.Error, nil)
		return responseDto, errors.New("page < 0")
	}
	var tempUsers []User
	var users []*UserInfoResponseDto
	var count int64
	if dto.Username == "" {
		apps.DB.Limit(dto.PageSize).Offset((dto.PageNum - 1) * dto.PageSize).Find(&tempUsers).Limit(-1).Offset(-1).Count(&count)
	} else {
		apps.DB.Where("username like '%" + dto.Username + "%' ").Limit(dto.PageSize).Offset((dto.PageNum - 1) * dto.PageSize).Find(&tempUsers).Limit(-1).Offset(-1).Count(&count)
	}
	for i := range tempUsers {
		var t = &UserInfoResponseDto{}
		t.format(tempUsers[i])
		users = append(users, t)
	}
	responseDto.Init(dto.GetDataLimitDto, count, users)
	return responseDto, err
}

type CreateUserDto struct {
	Username string `json:"username" binding:"required,min=6,max=32" label:"用户名"`
	Password string `json:"password" binding:"required,min=6,max=32" label:"密码"`
}

// Create 创建User
func (dto *CreateUserDto) Create(c *gin.Context) (userInfo UserInfoResponseDto, err error) {
	model := &User{
		Username: dto.Username,
	}
	model.SetPassword(dto.Password)

	result := apps.DB.Create(model)
	err = result.Error
	if err != nil {
		utils.Response(
			c,
			"插入失败err: "+err.Error(),
			utils.Status.Error,
			nil,
		)
	} else {
		userInfo.format(*model)
	}
	return userInfo, err
}

type DeleteUserDto struct {
	Id int `binding:"required"`
}

func (dto *DeleteUserDto) DeleteUser(c *gin.Context) (err error) {
	result := apps.DB.Delete(&User{Id: dto.Id})
	err = result.Error
	if err != nil {
		utils.Response(
			c,
			"删除失败err: "+err.Error(),
			utils.Status.Error,
			nil,
		)
	}
	return err
}

type UserInfoResponseDto struct {
	Id        int       `json:"id"`
	Username  string    `json:"username"`
	IsBackUp  bool      `json:"is_back_up"`
	IsSuper   bool      `json:"is_super"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func (dto *UserInfoResponseDto) format(user User) {
	dto.Id = user.Id
	dto.Username = user.Username
	dto.IsBackUp = user.IsBackUp
	dto.IsSuper = user.IsSuper
	dto.CreatedAt = user.CreatedAt
	dto.UpdatedAt = user.UpdatedAt
}

type SetSuperDto struct {
	Id      int  `json:"id" binding:"required"`
	IsSuper bool `json:"is_super"`
}

// SetSuper 设置超级用户
func (dto *SetSuperDto) SetSuper(c *gin.Context) (user *User, err error) {
	user = &User{Id: dto.Id}
	result := apps.DB.Model(&user).
		Update("is_super", dto.IsSuper).
		Where(&user).
		Find(&user)
	err = result.Error
	if err == nil && result.RowsAffected == 0 {
		err = errors.New("该数据以删除，或没有该ID")
	}
	if err != nil {
		utils.Response(
			c,
			"更新失败err: "+err.Error(),
			utils.Status.Error,
			nil,
		)
	}
	return user, err
}

type BackUpUserDto struct {
	Id       int  `json:"id" binding:"required"`
	IsBackUp bool `json:"is_back_up"`
}

// BackUpUser 停用User
func (dto *BackUpUserDto) BackUpUser(c *gin.Context) (user *User, err error) {
	user = &User{Id: dto.Id}
	result := apps.DB.Model(&user).First(&user)
	if user.Username == "admin666" {
		err = errors.New("admin666，不可停用")
	} else {
		result = apps.DB.Model(&user).Update("is_back_up", dto.IsBackUp).Where(&user).First(&user)
		err = result.Error
		if err == nil && result.RowsAffected == 0 {
			err = errors.New("该数据以删除，或没有该ID")
		}
	}
	if err != nil {
		utils.Response(
			c,
			"更新失败err: "+err.Error(),
			utils.Status.Error,
			nil,
		)
	}
	return user, err
}

type EditPasswordDto struct {
	Id       int    `json:"id" binding:"required"`
	Password string `json:"password" binding:"required"`
}

// EditPassword 更新User密码
func (dto *EditPasswordDto) EditPassword(c *gin.Context) (err error) {
	user := &User{Id: dto.Id}
	result := apps.DB.Where(user).Find(&user)
	println(user)
	if result.Error == nil && len(user.Username) > 0 {
		user.SetPassword(dto.Password)
		result = apps.DB.Save(&user)
		err = result.Error
	}
	if err == nil && result.RowsAffected == 0 {
		err = errors.New("该数据以删除，或没有该ID")
	}
	if err != nil {
		utils.Response(
			c,
			"更新失败err: "+err.Error(),
			utils.Status.Error,
			nil,
		)
	}
	return err
}
